/* 
 * File:   Player.cpp
 * Author: mauro
 * 
 * Created on 12 de octubre de 2014, 15:49
 */

#include "Player.h"

Player::Player(int id) {
    this->number = id;
}
//el jugador se inicializa despues de que el servidor le envio los datos del nievel
//del juego

Player::Player(ServerSocket &server, int socket, ObjFactory *objFactory, int number, float posX, float posY, string namePlayer) {
    this->socket = socket;
    this->init = false;
    this->crashed = true; // solo false si se desconecta el cliente
    this->server = server;
    this->number = number;
    //    this->semaforo = false; //recv esta inicial
    this->character = new Character(objFactory, posX, posY, number);
    this->setHasBody(true);
    this->character->setCharacterId(number);
    this->equals = false;
    this->name = namePlayer;
    string fileName;
    fileName.append("Doc/PlayerFiles/player");
    fileName.append(this->name);
    fileName.append(".json");
    this->fileName = fileName;

    this->posIniX = posX;
    this->posIniY = posY;
    //    this->alive = true;
}

void Player::reviveCharacter(ObjFactory *objFactory) {
    //    cout << "character ID  " << this->number << endl;
    this->character = new Character(objFactory, this->posIniX, this->posIniY, this->number);

    //    cout << "fin character new id: " << this->character->getData().idCharacter << endl;
}

bool Player::isInit() {
    return this->init;
}

void Player::restartPlayer() {
    this->character->restartPlayer();
}


bool Player::hasCrashed() {
    return this->crashed;
}

int Player::getSocket() {
    return this->socket;
}

void Player::setInit(bool stat) {
    this->init = stat;
}

void Player::setCrash(bool stat) {
    this->character->die();
    this->crashed = stat;
}

void Player::pushEvent(struct Event event) {
    this->events.push(event);
}

bool Player::emptyEvent() {
    return this->events.empty();
}

void Player::communicationRecv() {
    //temporal
    struct Event ee;
    ee.pressed = '@';

    int n;
    int timeOut = 8;
    while (this->isInit()) {
        struct Event e;
        int length = sizeof (e);
        n = this->server.recvTimeOut(this->socket, &e, length, timeOut);
        if (n == -1) {
            //            cout<<"exit RECV "<<endl;
            myLogFile->writeInLog(2, "[2] Error interno del socket al recibir evento del jugador.\n");

            this->setInit(false);
            break;
        }
        if (n == -2) {
            //                        cout << "El temporizador vencio de REcv" << endl;
            myLogFile->writeInLog(1, "[1] El jugador se desconecto mientras se recibía su evento, se guardará la partida.\n");

            this->setCrash(true);
            this->saveGame();
            this->setInit(false);
            this->pushEvent(ee);
            break;
        }
        if (n == 0) {
            myLogFile->writeInLog(1, "[1] El jugador se desconecto mientras se recibía su evento, se guardará la partida.\n");
            //                        cout << "Se fue el cliente" << endl;

            this->setCrash(false);
            this->setInit(false);
            this->pushEvent(ee);
            this->saveGame();
            break;
        } else if (n != GARBAGE) {
            //            cout << "Llego evento: " << e.pressed<< " "<< e.released << " DE PLAYER "<< this->getCharacterId()<<endl;
            this->pushEvent(e);
        }
    }
}

void Player::runThreadRecv() {
    std::thread thread(&Player::communicationRecv, this);
    std::swap(thread, this->threadRecv);
}

void Player::communicationSend() {
    int n;
    while (this->isInit()) {
        //Aca habria que popear de la cola bloqueante de ObjectInit
        struct ObjectInit myObj;
        myObj = this->popObjects();
        int length = sizeof (myObj);
        if (this->isInit()) {
            n = this->server.sendAll(this->socket, &myObj, length);
        }
        if (n == -1) {
            //cout << "Error enviar" << endl;
            myLogFile->writeInLog(2, "[2] Error interno del socket al enviar elemento del mapa al jugador.\n");
            break;
        }
        if (n == 0) {
            myLogFile->writeInLog(1, "[1] El jugador se desconecto mientras se le enviaba un elemento del mapa, se guardará la partida.\n");
            this->saveGame();
            //cout << "Se fue el cliente" << endl;
            break;
        } else {
            //            if (myObj.idCharacter != -1) {
            //                cout << "send myObj.idCharacter-> " << myObj.idCharacter << " myObj.objectId-> " << myObj.objectId << " myObj.level-> " << myObj.level << endl;
            //            }
        }

    }

}

void Player::runThreadSend() {
    std::thread thread(&Player::communicationSend, this);
    std::swap(thread, this->threadSend);
}

void Player::sendPulse() {
    while (this->isInit()) {
        usleep(50000);
        //        cout << "Enviando pulso" << endl;

        struct ObjectInit fin;
        fin.idCharacter = ID_END_SEND;
        this->pushObjects(fin);
    }
}

void Player::runThreadPulse() {
    std::thread thread(&Player::sendPulse, this);
    std::swap(thread, this->threadPulse);
}

void Player::pushObjects(struct ObjectInit buffer) {
    //    cout<<"size Event: "<<this->objects.size()<<endl;    
    this->objects.push(buffer);
}

bool Player::getAreEquals() {
    return this->character->getAreEquals();
    //    return false;
}

struct ObjectInit Player::popObjects() {
    struct ObjectInit obj;
    obj = (struct ObjectInit) this->objects.waitAndPop();

    return obj;
}

int Player::getState() {
    return this->character->getState();
}

string Player::getName() {
    return this->name;
}

int Player::getScore() {
    return this->character->getScore();
}

int Player::getLifes() {
    return this->character->getLifes();
}

int Player::getCharacterId() {
    return this->character->getData().idCharacter;
}

void Player::stopWalking() {
    return this->character->stopWalking();
}

void Player::stopKneeling() {
    return this->character->stopKneeling();
}

void Player::keepJumping() {
    return this->character->keepJumping();
}

void Player::keepKneeling() {
    return this->character->keepKneeling();
}

void Player::fallLeft() {
    return this->character->fallLeft();
}

void Player::fallRight() {
    return this->character->fallRight();
}

void Player::keepFallingDown() {
    return this->character->keepFallingDown();
}

void Player::keepWalking() {
    return this->character->keepWalking();
}

void Player::jump() {
    // se libera al saltar
    this->trapped = false;

    return this->character->jump();
}

bool Player::isWalkingRight() {
    return this->character->isWalkingRight();
}

bool Player::isWalkingLeft() {
    return this->character->isWalkingLeft();
}

bool Player::isWatching() {
    return this->character->isWatching();
}

void Player::walkLeft() {
    if (this->trapped)
        return;

    return this->character->walkLeft();
}

void Player::walkRight() {
    if (this->trapped)
        return;

    return this->character->walkRight();
}

void Player::kneel() {
    if (this->trapped)
        return;

    return this->character->kneel();
}

void Player::wait() {
    return this->character->wait();
}

float Player::getPosX() {
    return this->character->getPosX();
}

float Player::getPosY() {
    return this->character->getPosY();
}

int Player::getPixelsX() {
    int pixel;
    pixel = (this->character->getPosX()*100);
    return pixel;
}

int Player::getPixelsY() {
    int pixel;
    pixel = (this->character->getPosY()*100);
    return pixel;
}

bool Player::saveGame() {
    myLogFile->writeInLog(1, "[1] Partida guardada en ");
    myLogFile->writeInLog(1, this->fileName);
    myLogFile->writeInLog(1, "\n");
    //    cout << "this->filename" << this->fileName << endl;
    //    cout << "this->number: " << this->number << endl;
    ofstream theFile;
    theFile.open(this->fileName, ios_base::trunc);
    // obj:: objeto de json para generar en memoria la estructura
    JSON::Object o, o1;
    o1["id"] = this->number; //chequear si number es el id del jugador
    o1["x"] = this->getPixelsX();
    //aca hay que poner la ultima posicion del personaje en x
    o1["y"] = this->getPixelsY(); //aca hay que poner la ultima posicion del personaje en y
    o1["ubic"] = this->getUbicDyn();
    o1["points"] = 1390; //en esta entrega esto va harcoded
    o1["lives"] = 5; //en esta entrega esto va harcoded

    o["personaje"] = o1;

    //    cout << o << endl;
    theFile << o;
    theFile.close();

}

void Player::updatePosition() {
    // implementa character
}

void Player::updateRotation() {
    // implementa character
}

void Player::updateEvent(struct Event e) {
    this->character->updateEvent(e);
}

void Player::sendLevelFinished(struct ObjectInit myObj) {
    this->pushObjects(myObj);
}

void Player::updateData() {
    this->character->updateData();
}

int Player::getUbicDyn() {
    return this->character->getUbicDyn();
}

void Player::setUbicDyn(int id) {
    this->character->setUbicDyn(id);
}

struct ObjectInit Player::getData() {
    this->updateData();
    return this->character->getData();
}

b2Body* Player::getBody() {
    return this->character->getBody();
}

void Player::checkChanges() {
    this->character->checkChanges();
}

bool Player::hasBody() {
    return this->flagHasBody;
}

void Player::setHasBody(bool b) {
    this->flagHasBody = b;
}

bool Player::tryPop(struct Event &e) {
    bool r = this->events.tryPop(e);
    //    struct Event ee = this->events.waitAndPop(this->isInit());
    return r;
}

void Player::checkFallingDown() {
    this->character->isFallingDown();
}

struct Event Player::popEvent() {
    //    cout <<"SIZE "<<this->events.size()<<endl;
    struct Event e;
    e = this->events.waitAndPop(this->isInit());

    return e;
}
//SHOOT

SnowBall* Player::shoot() {
    //    cout << " Player shoot!" << endl;
    SnowBall *bullet = this->character->shoot();
    return bullet;
    //guardar en un nuevo vector de dinamicos para enviar al cliente.    
}

bool Player::isInContactBonus() {

    return (this->character->isInContactBonus());
}

bool Player::isInContactEnemy() {
    // si esta en la crazy ball no pueden matarlo
    if (this->trapped)
        return false;

    return (this->character->isInContactEnemy());
}

bool Player::isInContactSnowBallEnemy() {
    if (this->trapped)
        return false;

    return (this->character->isInContactSnowBallEnemy());
}

bool Player::isInContactCrazyBall() {
    // no puede atraparlo mas de una
    if (this->trapped)
        return false;

    return (this->character->isInContactCrazyBall());
}

void Player::setTrapped() {
    if (!this->trapped) {
        this->trapped = true;
    }
}

void Player::kill() {
    this->character->kill();
}

void Player::jumpingRight() {
    this->character->jumpingRight();
}

void Player::jumpingLeft() {

    this->character->jumpingLeft();
}

void Player::changeToPosInitial() {
    this->character->changeToPosInitial();
}

//bool Player::isAlive() {
//    if (this->character->getLifes() < 0) {
//        this->alive = false;
//    }
//    return this->alive;
//}

Player::~Player() {
    //    cout << "Destructor de player\n";
}

